//多进程服务端TCP通信
#include<stdio.h>
//字节序
#include<arpa/inet.h>
//socket通信
#include <sys/types.h>
#include <sys/socket.h>
//exit
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
//信号捕捉，子进程回收
#include<errno.h>
#include <signal.h>
#include <sys/wait.h>

void recyleChild(int arg) {
    while(1) {
        int ret = waitpid(-1, NULL, WNOHANG);
        if(ret == -1) {
            // 所有的子进程都回收了
            break;
        }else if(ret == 0) {
            // 还有子进程活着
            break;
        } else if(ret > 0){
            // 被回收了
            printf("child process %d is reback..\n", ret);
        }
    }
}
int main() {
    printf("start server.....");
    //定义act相关参数
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = recyleChild;
    //注册信号捕捉
    sigaction(SIGCHLD,&act,NULL);

    //创建socket,准备好FD资源-   9999
    int lfd = socket(AF_INET,SOCK_STREAM,0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }
    //绑定本机ip地址和端口，
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;
    //绑定FD资源与端口号进行关联
    int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }
    //监听连接，他需要去做管理
    //资源  TCP协议的规矩去进行使用FD
    //三次握手成交成一个连接之后，我才是用具体的资源进行数据发送
    //aaa
    //1，2，3走报文
    //4 push 走数据
    //FD在绑定端口之后，应该要有一个状态
    //当前资源是否可用，是否已经完成三次握手
    //socket实现过程：
    //    维护两个队列
    //   等待队列，没有完成三次握手的FD资源管理
    //   完成队列，完成了三次握手的FD资源管理
    //backlog    等待队列长度
    //维护一个队列。 用来管理当前这个socket握手完成之后的资源
    //等待队列
    //完成队列
    ret = listen(lfd,8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }
    //循环接收客户端连接
    list lst ;
    while(1) {

        struct sockaddr_in caddr;
        int len = sizeof(caddr);

       if(ldf > 0){
           int cfd = accept(lfd, (struct sockaddr*)&caddr, reinterpret_cast<socklen_t *>(&len));
           lst.add(cfd);
       }

        //accept之前的所有的流程都是针对于去准备一个直接应用来进行 使用的FD
        if(cfd == -1) {
            if(errno == EINTR) continue;
            perror("accept");
            exit(-1);
        }

        //创建子进程，输出客户端信息并进行通信
        //子进程创建目的，如果是统一进程下，函数、线程的应用在栈区来讲是在同一地址空间下，而且执行是，
        // 大多只对于其相关的一些数据进行处理，如果算法存在某些错误，
        // 将有可能破坏同处于一个地址空间的一些重要内容
        //这里类似理解为多线程开辟
        //子进程 = java线程
        pid_t spid = fork();
//        new Thread(()->{
//            //.....
//        }).start()

        if(spid == 0) {
            //子进程
            //输出客户端ip 和端口号
            //代码写这里就OK
            char cip[16];
            inet_ntop(AF_INET,&caddr.sin_addr.s_addr,cip,strlen(cip));
            unsigned short cport = ntohs(caddr.sin_port);
            printf("Client ip is %s and port is %d\n",cip,cport);
            //创建接收缓冲区char revbuf[1024];，用户缓冲区
            //cfd  内核缓冲区
            while(1) {
                //接收客户端信息，
                //释放CPU资源，posix-内核的
                int rlen = read(cfd,revbuf,sizeof(revbuf));
                list.add(rlen);

                //自己去读当前开出来的FD资源，遍历所有的FD资源
//                fori(int i = 0 ;i < lst.size();i++){
//                    int fd = lst[i];
//                       if(fd.leng > 0 ){
//
//                       }
//                    //....
//                }

                //这种方案：CPU的计算资源被疯狂的拉大，IO --->?判定是否有数据
                //这种好处是我绕过了OS的资源限制，我有钱我可以随便搞。1024核
                if(rlen == -1) {
                    perror("read");
                    exit(-1);
                } else if(rlen > 0) {
                    printf("Sever have recieved :%s\n",revbuf);
                } else if(rlen == 0) {
                    printf("client have closed..\n");
                    break;
                }
                sleep(1);
                //发送信息给客户端
                write(cfd,revbuf,strlen(revbuf)+1);//加1是为了把 字符串终止符发送过去 以免产生bug(该bug跟字符串长短相关)
            }
            //关闭客户端文件描述符
            close(cfd);
            //退出当前子进程
            exit(0);
        }
    }

    //关闭监听描述符
    close(lfd);

    return 0;
}
